package com.zhihaoliang.coroutine.buss.suspend

import com.zhihaoliang.coroutine.base.BaseActivity
import com.zhihaoliang.coroutine.databinding.FunctionActivitySuspendBinding
import com.zhihaoliang.coroutine.util.log
import kotlinx.coroutines.*
import kotlin.system.measureTimeMillis

/**
 * @author 支豪亮
 * @date 2022/10/18 14:47
 * @des suspend function 介绍
 */
class SuspendFunctionActivity : BaseActivity<FunctionActivitySuspendBinding>(), ISuspendFunction {

    private val mainScope = MainScope()

    override fun initDataBinding() = FunctionActivitySuspendBinding.inflate(layoutInflater)

    override fun bindData() {
        super.bindData()
        dataBinding.suspendFunction = this
    }

    override fun onSequentialDefault() = onClick {
        val time = measureTimeMillis {
            val one = doSomethingUsefulOne()
            val two = doSomethingUsefulTwo()
            log("The answer is ${one + two}")
        }
        log("Completed in $time ms")
    }

    override fun onAsyncOperation()  {
        mainScope.launch {
            val time = measureTimeMillis {
                val one = async { doSomethingUsefulOne() }
                val two = async { doSomethingUsefulTwo() }
                log("The answer is ${one.await() + two.await()}")
            }
            log("Completed in $time ms")
        }
    }

    override fun lazyOperation()  {
        mainScope.launch {
            val time = measureTimeMillis {
                val one = async(start = CoroutineStart.LAZY) { doSomethingUsefulOne() }
                val two = async(start = CoroutineStart.LAZY) { doSomethingUsefulTwo() }
                one.start() // 如果没有 则在 one.await() 开始
                log("The answer is ${one.await() + two.await()}")
            }
            log("Completed in $time ms")
        }
    }

    /**
     * 这种带有异步函数的编程风格仅供参考，因为这在其它编程语言中是一种受欢迎的风格。在 Kotlin 的协程中使用这种风格是强烈不推荐的， 原因如下所述
     *
     * 考虑一下如果 val one = somethingUsefulOneAsync() 这一行和 one.await() 表达式这里在代码中有逻辑错误， 并且程序抛出了异常以及程序在操作的过程中中止，
     * 将会发生什么。 通常情况下，一个全局的异常处理者会捕获这个异常，将异常打印成日记并报告给开发者，但是反之该程序将会继续执行其它操作。
     * 但是这里我们的 somethingUsefulOneAsync 仍然在后台执行， 尽管如此，启动它的那次操作也会被终止。这个程序将不会进行结构化并发，如下一小节所示。
     *
     */
    override fun asyncStyleErro() {
        log("Completed in asyncStyleErro")
        val time = measureTimeMillis { // 我们可以在协程外面启动异步执行
            val one = somethingUsefulOneAsync()
            val two = somethingUsefulTwoAsync() // 但是等待结果必须调用其它的挂起或者阻塞 // 当我们等待结果的时候，这里我们使用 `runBlocking { …… }` 来阻塞主线程
            mainScope.launch {
                log("The answer is ${one.await() + two.await()}")
            }
        }
        log("Completed in $time ms")
    }

    override fun asyncStyleRight() {
        mainScope.launch {
            try {
                failedConcurrentSum()
            } catch (e: ArithmeticException) {
                log("Computation failed with ArithmeticException",coroutineContext)
            }
        }
    }

    suspend fun failedConcurrentSum(): Int = coroutineScope {
        val one = async<Int> {
            try {
                delay(Long.MAX_VALUE) // 模拟一个长时间的运算
                42
            } finally {
                log("First child was cancelled",coroutineContext)
            }
        }
        val two = async<Int> {
            log("Second child throws an exception",coroutineContext)
            throw ArithmeticException()
        }
        one.await() + two.await()
    }

    private fun somethingUsefulOneAsync() = mainScope.async {
        doSomethingUsefulOne()
    }

    // somethingUsefulTwoAsync 函数的返回值类型是 Deferred<Int>
    private fun somethingUsefulTwoAsync() = mainScope.async {
        doSomethingUsefulTwo()
    }

    private suspend fun doSomethingUsefulOne(): Int {
        log("doSomethingUsefulOne start")
        delay(1000L) // pretend we are doing something useful here
        log("doSomethingUsefulOne end")
        return 13
    }

    private suspend fun doSomethingUsefulTwo(): Int {
        log("doSomethingUsefulTwo start")
        delay(5000L) // pretend we are doing something useful here, too
        log("doSomethingUsefulTwo end")
        return 29
    }

    private fun onClick(method: suspend () -> Unit) {
        mainScope.launch {
            method.invoke()
        }
    }


}
